package display3D.objet3D
{
	import Tools.Geometry;
	
	import alternativa.engine3d.core.Object3DContainer;
	import alternativa.engine3d.materials.FillMaterial;
	import alternativa.engine3d.primitives.Box;
	import alternativa.engine3d.primitives.Plane;
	
	import flash.events.Event;
	
	import org.papervision3d.scenes.Scene3D;

	/**
	 * Classe représentant la salle 3D
	 * @author Arnaud
	 */
	public class Salle3D
	{
		
		private var _murs:Vector.<Mur>;
		private var _floor: Floor;
		private var _roof: Roof;
		private var _id:String;
		private var _nom:String;
		private var _position_x:int;
		private var _position_y:int;
		
		/**
		 * Constructeur
		 * @param id Identifiant de la salle
		 * @param nom Nom de la salle
		 * @param positon_x Position x de depart de la salle
		 * @param position_y Position y de depart de la salle
		 * @param murs Murs de la salle
		 * @param floor Sol de la salle
		 * @param roof Plafond de la salle
		 */
		public function Salle3D(id:String,nom:String,positon_x:int,position_y:int,murs:Vector.<Mur>,floor:Floor,roof:Roof)
		{
			this._id = id;
			this._nom = nom;
			this._murs = murs;
			this._floor = floor;
			this._roof = roof;
			this._position_x = positon_x;
			this._position_y = position_y;
		}
		
		/**
		 * Dessine la salle sur la scene
		 * @param scene Conteneur de notre salle3D
		 */
		public function draw(scene:Object3DContainer):void
		{
			for(var i:int = 0; i<_murs.length; i++)
			{
				_murs[i].draw(scene);
			}
			
			_floor.draw(scene);
			_roof.draw(scene);
			
		}
		
		/**
		 * Retourne une porte en fonctionde son identifiant
		 * @param idPorte Identifiant de la porte
		 * @return La porte ou null
		 * @see Porte
		 */
		public function getPorteById(idPorte:String):Porte
		{
			for each(var mur:Mur in _murs)
			{
				if (mur.portes[idPorte] != null)
				{
					return mur.portes[idPorte];
				}
			}
			
			return null;
		}
		
		/**
		 * Retourne le mur qui contient la porte
		 * @param idPorte Identifiant de la porte
		 * @return Le mur ou null
		 * @see Porte
		 * @see Mur
		 */
		public function getMurByIdPorte(idPorte:String):Mur
		{
			for each(var mur:Mur in _murs)
			{
				if (mur.portes[idPorte] != null)
				{
					return mur;
				}
			}
			
			return null;
		}

		/**
		 * Supprime la salle de la scene3D
		 * @param scene Conteneur de notre salle3D
		 */
		public function supprimerSalle(scene:Object3DContainer):void
		{
			for each(var mur:Mur in _murs)
			{
				mur.supprimerMur(scene);
			}
			
			_floor.supprimerFloor(scene);
			_roof.supprimerFloor(scene);
			
		}
		
		/**
		 * Supprime les transitions de notre salle3D
		 * @param scene Conteneur de notre salle3D
		 * @see Transition
		 */
		public function supprimerTransition(scene:Object3DContainer):void
		{
			for each(var mur:Mur in _murs)
			{
				for each(var porte:Porte in mur.portes)
				{
					porte.supprimerTransition(scene);
				}
			}
		}
		
		/**
		 * Envoi un événement lorsque une oeuvre est approché à moins de 300 pixel
		 * @param x Position caméra x
		 * @param y Postion caméra y
		 * @param z Position caméra z
		 * @see Peinture
		 */
		public function detecterOeuvre(x:*,y:*,z:*):void
		{
			for each(var mur:Mur in _murs)
			{
				for each(var oeuvre:Peinture in mur.oeuvres)
				{
					if (Geometry.distance(x,y,oeuvre.x,oeuvre.y)<300)
					{
						oeuvre.dispatchEvent(new Event(Peinture.OEUVRE_PROXIMITE));		
					}
				}
			}
		}
		
		/**
		 * Envoi un événement lorsque un point de transition est approché à moins de 80 pixel
		 * @param x Position caméra x
		 * @param y Postion caméra y
		 * @param z Position caméra z
		 * @return Renvoi true si transition détecté
		 * @see Transition
		 */
		public function detecterTransition(x:*,y:*,z:*):Boolean
		{
			for each(var mur:Mur in _murs)
			{
				for each(var porte:Porte in mur.portes)
				{
					if (porte.transition.charged)
						{
						var cont1x = porte.transition.pointContact[0][0];
						var cont1y = porte.transition.pointContact[0][1];
							
						var cont2x = porte.transition.pointContact[1][0];
						var cont2y = porte.transition.pointContact[1][1];
						
						var cont1xb = porte.transition.pointContact2[0][0];
						var cont1yb = porte.transition.pointContact2[0][1];
						
						var cont2xb = porte.transition.pointContact2[1][0];
						var cont2yb = porte.transition.pointContact2[1][1];
							
						if ((Geometry.distance(x,y,cont1x,cont1y)<80 || Geometry.distance(x,y,cont2x,cont2y)<80) && porte.transition.entrer)
						{
								porte.dispatchEvent(new Event(Porte.PORTE_OUVERTE));
								porte.transition.entrer = false;
								return true;
						}
						else if((Geometry.distance(x,y,cont1xb,cont1yb)<40 || Geometry.distance(x,y,cont2xb,cont2yb)<40) && !porte.transition.entrer)
						{
							porte.transition.entrer = true ;
							return false;
						}
					}
				}
			}
		}
		
		/**
		 * Effectue la transformation et la rotation vectoriel pour permettre à la nouvelle salle de se mettre en face de l'ancienne
		 * @param pointDepart Point de la salle de depart
		 * @param pointArrive Point de la salle d'arrivé
		 * @param porteCible Porte cible de la salle d'arrivé
		 */
		public function transformation(pointDepart:Array,pointArrive:Array,porteCible:String)
		{
			//On calcul le vecteur entre le point de depart et le point de la porte cible
			var vecteurTransformation:Array = Geometry.coordonneVecteur(pointDepart[0],pointDepart[1],pointArrive[0],pointArrive[1]);
			for(var i:int = 0; i<_murs.length; i++)
			{
				_murs[i].transformationVectoriel(vecteurTransformation);
			}
			
			_floor.transformationVectoriel(vecteurTransformation);
			_roof.transformationVectoriel(vecteurTransformation);
			
			//On calcul la rotation que doivent subir nos points
			
			//Vecteur normalisé acien mur
			var vecteurNormMurDep:Array = pointArrive[2];
			
			var mur:Mur = getMurByIdPorte(porteCible);
			//Calcul des coordonnées de la porte
			var v:Array = Geometry.coordonneVecteur(mur.x1,mur.y1,mur.x2,mur.y2);
			var longueurMur:* = Geometry.distance(mur.x1,mur.y1,mur.x2,mur.y2);
			
			//Vecteur normalisé mur cible
			var vN:Array = new Array;
			vN[0] = v[0]/longueurMur;
			vN[1] = v[1]/longueurMur;
			
			
			//On fait le produit scalaire des deux vecteur normalisé pour trouver l'angle de rotation
			var rotationD:* = (vN[0]*vecteurNormMurDep[0])+(vN[1]*vecteurNormMurDep[1]);
			
			//On calcul la rotation 
			
			//Point sur le mur
			var tabAngle:Array = new Array;
			tabAngle[0] = Math.acos(Math.abs(rotationD));
			tabAngle[1] = -(Math.PI-Math.acos(Math.abs(rotationD)));
			tabAngle[2] =- Math.acos(Math.abs(rotationD));
			tabAngle[3] = Math.PI-Math.acos(Math.abs(rotationD));
			
			for (var i:int=0;i<4;i++)
			{
				var tempX:* = mur.x1;
				var tempZ:* = mur.y1;
				var angle:* = tabAngle[i];
				var porte:Porte = getPorteById(porteCible);
				var ptRotationXPorteCible:* =  Math.cos(angle)*(porte.x-pointArrive[0]) - Math.sin(angle)*(porte.y-pointArrive[1]) + pointArrive[0];
				var ptRotationYPorteCible:* = Math.sin(angle)*(porte.x-pointArrive[0]) + Math.cos(angle)*(porte.y-pointArrive[1]) + pointArrive[1];
				
				var ptRotationX:* =  Math.cos(angle)*(tempX-pointArrive[0]) - Math.sin(angle)*(tempZ-pointArrive[1]) + pointArrive[0];
				var ptRotationY:* = Math.sin(angle)*(tempX-pointArrive[0]) + Math.cos(angle)*(tempZ-pointArrive[1]) + pointArrive[1];
				var distMn:* = Geometry.distance(ptRotationX,ptRotationY,pointArrive[0],pointArrive[1]);				
				//Vecteur avec la rotation du point
				var vectRotation:Array = Geometry.coordonneVecteur(ptRotationX,ptRotationY,pointArrive[0],pointArrive[1]);
				//Vecteur avec la rotation perpendiculaire
				var vectRotationNorm:Array = new Array;
				vectRotationNorm[0] =vectRotation[0]/distMn;
				vectRotationNorm[1] =vectRotation[1]/distMn;
				
				var point1n:Array = new Array;
				var point2n:Array = new Array;
				
				point1n[0] = pointArrive[0] -(vectRotationNorm[0]);
				point1n[1] = pointArrive[1] -(vectRotationNorm[1]);
				
				point2n[0] = pointArrive[0] +(vectRotationNorm[0]);
				point2n[1] = pointArrive[1] +(vectRotationNorm[1]);
				
				var distMn2:* = Geometry.distance(point1n[0],point1n[1],point2n[0],point2n[1]);
				
				//Vecteur perpendiculaire au mur
				var vPern:Array = new Array;
				vPern[0] = -(point1n[1] - point2n[1]);
				vPern[1] = (point1n[0] - point2n[0]); 
				
				//Vecteur normalisé perpendiculaire
				var vNpern:Array = new Array;
				vNpern[0] = vPern[0]/distMn2;
				vNpern[1] = vPern[1]/distMn2;	
				
				//Ancien mur (vecteur de reference)
				//Calcul du vecteur perpendiculaire à l'ancien mur
				var point1:Array = new Array;
				var point2:Array = new Array;
				
				point1[0] = pointArrive[0] -(vecteurNormMurDep[0]);
				point1[1] = pointArrive[1] -(vecteurNormMurDep[1]);
				
				point2[0] = pointArrive[0] +(vecteurNormMurDep[0]);
				point2[1] = pointArrive[1] +(vecteurNormMurDep[1]);
				var distM:* = Geometry.distance(point1[0],point1[1],point2[0],point2[1]);
				
				//Vecteur perpendiculaire au mur
				var vPer:Array = new Array;
				vPer[0] = -(point1[1] - point2[1]);
				vPer[1] = (point1[0] - point2[0]); 
				
				//Vecteur normalisé perpendiculaire
				var vNper:Array = new Array;
				vNper[0] = vPer[0]/distM;
				vNper[1] = vPer[1]/distM;
				
				
				if(pointArrive[0]+vNper[0]*500==ptRotationXPorteCible-vNpern[0]*500 && pointArrive[1]+vNper[1]*500==ptRotationYPorteCible-vNpern[1]*500)
				{
					for(var i:int = 0; i<_murs.length; i++)
					{
						_murs[i].transformationRotation(pointArrive,angle);
					}
					
					_floor.transformationRotation(pointArrive,angle);
					_roof.transformationRotation(pointArrive,angle);
					return;
				}else if(pointArrive[0]+vNper[0]*-500==ptRotationXPorteCible-vNpern[0]*500 && pointArrive[1]+vNper[1]*-500==ptRotationYPorteCible-vNpern[1]*500)
				{
					for(var i:int = 0; i<_murs.length; i++)
					{
						_murs[i].transformationRotation(pointArrive,angle+Math.PI);
					}
					
					_floor.transformationRotation(pointArrive,angle+Math.PI);
					_roof.transformationRotation(pointArrive,angle+Math.PI);
					return;
				}else if(pointArrive[0]+vNper[0]*500==ptRotationXPorteCible-vNpern[0]*-500 && pointArrive[1]+vNper[1]*500==ptRotationYPorteCible-vNpern[1]*-500)
				{
					for(var i:int = 0; i<_murs.length; i++)
					{
						_murs[i].transformationRotation(pointArrive,angle+Math.PI);
					}
					
					_floor.transformationRotation(pointArrive,angle+Math.PI);
					_roof.transformationRotation(pointArrive,angle+Math.PI);
					return;
				}
			}
		}

		/**
		 * Contient tout les murs de la salle
		 */
		public function get murs():Vector.<Mur>
		{
			return _murs;
		}

		/**
		 * @private
		 */
		public function set murs(value:Vector.<Mur>):void
		{
			_murs = value;
		}

		/**
		 * Sol de la salle
		 */
		public function get floor():Floor
		{
			return _floor;
		}

		/**
		 * @private
		 */
		public function set floor(value:Floor):void
		{
			_floor = value;
		}

		/**
		 * Plafond de la salle
		 */
		public function get roof():Roof
		{
			return _roof;
		}

		/**
		 * @private
		 */
		public function set roof(value:Roof):void
		{
			_roof = value;
		}

		/**
		 * Identifiant de la salle
		 */
		public function get id():String
		{
			return _id;
		}

		/**
		 * @private
		 */
		public function set id(value:String):void
		{
			_id = value;
		}

		/**
		 * Nom de la salle
		 */
		public function get nom():String
		{
			return _nom;
		}

		/**
		 * @private
		 */
		public function set nom(value:String):void
		{
			_nom = value;
		}

		/**
		 * Coordonné x du point de départ de la salle
		 */
		public function get position_x():int
		{
			return _position_x;
		}

		/**
		 * @private
		 */
		public function set position_x(value:int):void
		{
			_position_x = value;
		}

		/**
		 * Coordonné y du point de départ de la salle 
		 */
		public function get position_y():int
		{
			return _position_y;
		}

		/**
		 * @private
		 */
		public function set position_y(value:int):void
		{
			_position_y = value;
		}


	}
}